package com.hrm.websocket;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.simple.JSONObject;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class WebSocketUtil {
	public static final String KEY_SHA = "SHA";
	private static final Log log = LogFactory.getLog(WebSocketUtil.class);

	public byte[] decryptBASE64(String key) throws IOException {
		return new BASE64Decoder().decodeBuffer(key);
	}

	private  String encryptBASE64(byte[] key) throws IOException {
		return new BASE64Encoder().encodeBuffer(key);
	}

	private  byte[] encryptSHA(byte[] data)
			throws NoSuchAlgorithmException {
		MessageDigest shaDigest = MessageDigest.getInstance(KEY_SHA);
		shaDigest.update(data);
		return shaDigest.digest();
	}

	private String handleHandShake(String key) {
		String constKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
		String tmpKey = key + constKey;
		byte[] data = tmpKey.getBytes();
		try {
			data = encryptSHA(data);
			return encryptBASE64(data);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";
	}

	public String warpConnectMessage(String key) {
		String thiskey = handleHandShake(key);
		String httpHeader = "HTTP/1.1 101 Switching Protocols\r\n";
		httpHeader += "Upgrade: websocket\r\n";
		httpHeader += "Connection: Upgrade\r\n";
		httpHeader += "Sec-WebSocket-Accept: " + thiskey + "\r\n";
		// 朄1�7后一行，丄1�7定只能加丄1�7丄1�7/r/n,否则向客户端发�1�7�消息的时�1�7�，客户端无法识刄1�7
		// 在chrome 浏览器中会返回Unrecognized frame opcode: 13. 这个错误
		return httpHeader;
	}
	

	public void disconnect() {
	}

	@SuppressWarnings({ "unused", "unchecked" })
	public byte[] parseWebSocketData(WebSocketSession session,ByteBuffer readBuffer) {
		int remainDataLength=readBuffer.limit();
		if(!session.isWriteBianry()){//如果是二进制写文件的消息
			// 第一个字芄1�7
			byte b = readBuffer.get();
			byte fin = (byte) (b & 0x80);// fin
			byte rsv = (byte) (b & 0x70);// rsv1,rsv2,rsv3
			byte opcode = (byte) (b & 0x0f);// opcode
			if (opcode == 8) {
				// 表示关闭，socket发�1�7�命令来关闭，和浏览器的关闭都会出现〄1�7
				if(session.getUploadedSize()>0&&session.getUploadedSize()<session.getUploadFileSize()){//如果是文件上传，且文件上传未完成，则删除该临时文件
					log.info("upload cancel,delete the tmp file="+session.getFilePath());
					File file = new File(session.getFilePath());
					if(file.exists()){
						file.delete();
					}
				}
				return null;
			}
			// 第二个字芄1�7
			byte b2 = readBuffer.get();
			byte mask = (byte) (b2 & 0x80);// mask
			int length = (byte) (b2 & 0x7f); // data 长度
			int payloadBytesSize = 0; // 数据长度扄1�7占用的字节数
			if (length == 126) {
				byte[] sizeBytes = new byte[2];// 如果昄1�76那么后面两字节表示一丄1�7进制的无符号敄1�7
				readBuffer.get(sizeBytes);
				length = (int) (sizeBytes[0] & 0xff) * 0x100
						+ (sizeBytes[1] & 0xff);
			} else if (length == 127) {
				byte[] sizeBytes = new byte[8];// 如果昄1�77那么后面4字节表示丄1�7丄1�7进制的无符号敄1�7
				readBuffer.get(sizeBytes);
				length = (sizeBytes[0] & 0xff) * 256*256*256*256*256*256*256
						+(sizeBytes[1] & 0xff)* 256*256*256*256*256*256
						+(sizeBytes[2] & 0xff)* 256*256*256*256*256
						+(sizeBytes[3] & 0xff)* 256*256*256*256
						+(sizeBytes[4] & 0xff)* 256*256*256
						+(sizeBytes[5] & 0xff)* 256*256
						+(sizeBytes[6] & 0xff)* 256
						+(sizeBytes[7] & 0xff);
			}
			
			byte[] temp=null;
			byte[] maskKey = new byte[4];
			readBuffer.get(maskKey);
			session.setMaskKey(maskKey);
			int position = readBuffer.position();
			if(length<=remainDataLength){//单条消息
				session.setMsgLength(0);
			}else{//如果消息标示的长度大于buffer中的数据长度，则设置session的消息长度为总长度减去当前内容长度
//					session.setMsgLength(length-remainDataLength+position);//消息总长度需要减去当前和消息头长度
				session.setDataTotalLength(length);//设置数据总长度
				session.setWriteBianry(true);
			}
			byte[] data = new byte[remainDataLength-position]; // 传�1�7�的数据
			readBuffer.get(data);
			int totalLength=session.getDataTotalLength();
			for (int i = 0; i < data.length; i++) {
				data[i] = (byte) (data[i] ^ maskKey[i % 4]);
			}
			if(opcode==2){//二进制消息
				File file = new File(session.getFilePath());
//				log.debug("check file is exist?result is "+(file.exists()?"exist":"not exist"));
				//如果当前upload未上传完成，则不能重命名已上传的文件
				if(session.getUploadedSize()==0||session.getUploadedSize()==session.getUploadFileSize()){//如果当前文件为新上传的，则需判断文件是否存在
					log.info("begin receive upload file data,File name:"+session.getFileName()+",Total size:"+length+";\nFile Path:"+session.getFilePath());
					if(file.exists()){
						file.renameTo(new File(session.getFilePath()+"."+new Date().getTime()+".old"));
						log.info("rename file name to "+session.getFilePath()+"."+new Date().getTime()+".old");//如果有相同文件名，则将旧文件名设置为当前时间+.old
					}
				}
				saveFileFromBytes(data, session.getFilePath());
				session.setWriteLength(data.length);//获取写入文件的总长度
				session.setUploadedSize(session.getUploadedSize()+data.length);//已上传数据总大小
				JSONObject jo=session.getJo();
				jo.put("timestamp", new Date().getTime());
				jo.put("state", "success");
				if(session.getWriteLength()==session.getDataTotalLength()){//如果文件内容小于一次传输的数据量，则说明文件是一次上传成功的
//					log.debug("receive data over in once time,notice client uploadProgress ok.");
					jo.put("uploadProgress", "ok");
					jo.put("filePath", session.getFilePath().split("webapps/")[1]);
					session.setWriteBianry(false);//设置是否二进制消息为否
					if(session.getUploadedSize()>=session.getUploadFileSize()){//说明文件一次上传成功
						jo.put("uploadProgress", "end");
						session.setWriteBianry(false);
					}
					return jo.toJSONString().getBytes();
				}else{
//					log.debug("receive block data over,this frame has more data,please continue,notice client uploadProgress progress and uploadedSize");
					//返回前台当前已上传的信息
					jo.put("uploadProgress", "progress");
					jo.put("uploadedSize", session.getUploadedSize());
					//return jo.toJSONString().getBytes();
					return null;
				}
			}
			return data;
			
		}else {//如果
			int writeLength=session.getWriteLength();
			byte[] data = new byte[remainDataLength]; // 传�1�7�的数据
			readBuffer.get(data);
			byte[] maskKey = session.getMaskKey();
			for (int i = 0; i < data.length; i++) {
				data[i] = (byte) (data[i] ^ maskKey[(writeLength+i) % 4]);
			}
			session.setWriteLength(session.getWriteLength()+data.length);
			session.setUploadedSize(session.getUploadedSize()+data.length);
			saveFileFromBytes(data, session.getFilePath());
			JSONObject jo=session.getJo();
//			log.debug(session.getWriteLength()+","+session.getDataTotalLength());
			if(session.getWriteLength()==session.getDataTotalLength()){//如果写入长度等于总长度，说明写入成功。返回上传的文件路径和地址等信息
				jo.put("state", "success");
				jo.put("timestamp", new Date().getTime());
				session.setWriteBianry(false);
				if(session.getUploadedSize()<session.getUploadFileSize()){//如果分块上传未完成，则继续
//					log.debug("receive data over in once time,notice client uploadProgress ok.");
					jo.put("uploadProgress", "ok");
					jo.put("uploadedSize", session.getUploadedSize());
				}else if(session.getUploadedSize()>=session.getUploadFileSize()){//分块上传完成，则通知用户
					jo.put("filePath", session.getFilePath().split("webapps/")[1]);
					jo.put("uploadProgress", "end");
					log.info("file upload end,file path="+session.getFilePath()+".");
					//重新设置文件上传状态
					session.setUploadedSize(0);
					session.setUploadFileSize(0);
				}
				return jo.toJSONString().getBytes();
			}else{
				jo.put("state", "success");
//				jo.put("filePath", session.getFilePath().split("webapps/")[1]);
				jo.put("timestamp", new Date().getTime());
				if(session.getUploadedSize()<session.getUploadFileSize()){//如果分块上传未完成，则继续
					jo.put("uploadProgress", "progress");
					jo.put("uploadedSize", session.getUploadedSize());
				}
				else if(session.getUploadedSize()>=session.getUploadFileSize()){//分块上传完成，则通知用户
					jo.put("uploadProgress", "end");
					jo.put("uploadedSize", session.getUploadedSize());
					session.setUploadedSize(0);
					session.setUploadFileSize(0);
					session.setWriteBianry(false);
				}
				return jo.toJSONString().getBytes();
			}
		}
		
		
	}
	
	public static boolean saveFileFromBytes(byte[] b, String outputFile)  
    {  
      FileOutputStream fstream = null;  
      File file = null;  
      try  
      {  
        file = new File(outputFile);  
        fstream = new FileOutputStream(file, true);  
        fstream.write(b);
      }  
      catch (Exception e)  
      {  
        e.printStackTrace();
        return false;
      }  
      finally  
      {  
        if (fstream != null)  
        {  
          try  
          {  
              fstream.close();  
          }  
          catch (IOException e1)  
          {  
            e1.printStackTrace();  
          }  
        }  
      }  
      return true;  
    }  
}
